import random
import tkinter  # UI模块
import tkinter.messagebox  # UI弹窗
from PIL import ImageTk, Image  # 图片处理


class Matrix2048(object):
    def __init__(self, column: int = 4):
        self.column = column
        self.score = 0
        self.init()

    def init(self):
        self.matrix = [[0 for _ in range(self.column)] for _ in range(self.column)]
        self.generate_number()
        self.generate_number()

    def show(self):
        r = '+-----' * self.column + '+\n'
        for li in self.matrix:
            for i in li:
                s = '|' + ' ' * 5 if i == 0 else '|' + str(i).center(5, ' ')
                r = r + s
            r = r + '|\n' + '+-----' * self.column + '+\n'
        print(r)

    def generate_number(self):
        # 找出所有为0的位置
        zero = [(x, y) for x in range(self.column)
                for y in range(self.column) if self.matrix[x][y] == 0]
        # 随机选择一个为0的位置并填充为随机数字
        if zero:
            x, y = random.choice(zero)
            self.matrix[x][y] = 2

    def move_left(self):
        # 数字左移
        def move_left():
            for row in range(self.column):
                while 0 in self.matrix[row]:
                    self.matrix[row].remove(0)
                while len(self.matrix[row]) != self.column:
                    self.matrix[row].append(0)

        def merge_left():
            for row in range(self.column):
                for col in range(self.column - 1):
                    if self.matrix[row][col] == self.matrix[row][col + 1] and self.matrix[row][col] != 0:
                        self.matrix[row][col] = 2 * self.matrix[row][col]
                        self.matrix[row][col + 1] = 0
                        self.score = self.score + self.matrix[row][col]
                        print(self.score)

        move_left()
        merge_left()
        move_left()

    def move_up(self):
        self.matrix = [[self.matrix[x][y]
                        for x in range(self.column)] for y in range(self.column)]
        self.move_left()
        self.matrix = [[self.matrix[x][y]
                        for x in range(self.column)] for y in range(self.column)]

    def move_down(self):
        self.matrix = self.matrix[::-1]
        self.move_up()
        self.matrix = self.matrix[::-1]

    def move_right(self):
        self.matrix = [li[::-1] for li in self.matrix]
        self.move_left()
        self.matrix = [li[::-1] for li in self.matrix]

    def gameover(self) -> bool:
        matrix = self.matrix
        column = self.column
        # 1. 矩阵(self.matrix)中有0则未结束
        if 0 in [i for li in matrix for i in li]:
            return False

        # 2. 水平方向上有相同的数字则未结束
        for row in range(column):
            for col in range(column-1):
                if matrix[row][col] == matrix[row][col+1]:
                    return False

        # 3. 垂直方向上有相同的数字则未结束
        for row in range(column-1):
            for col in range(column):
                if matrix[row][col] == matrix[row+1][col]:
                    return False
        return True

class Game2048(object):
    def __init__(self, column: int = 4):
        self.column = column
        self.cellSize = 80
        self.spaceSize = 12
        self.elements = []
        self.elementScore=None
        self.style = {
            'page': {'bg': '#d6dee0', },
            # 0 ~ 4 灰色系  bg 背景颜色， fg字体颜色， fz字体大小
            0:    {'bg': '#EEEEEE', 'fg': '#EEEEEE', 'fz': 30},
            2**1: {'bg': '#E5E5E5', 'fg': '#707070', 'fz': 30},
            2**2: {'bg': '#D4D4D4', 'fg': '#707070', 'fz': 30},
            # 8 ～ 16 橙色系
            2**3: {'bg': '#FFCC80', 'fg': '#FAFAFA', 'fz': 30},
            2**4: {'bg': '#FFB74D', 'fg': '#FAFAFA', 'fz': 30},
            # 32 ～ 64 红色系
            2**5: {'bg': '#FF7043', 'fg': '#FAFAFA', 'fz': 30},
            2**6: {'bg': '#FF5722', 'fg': '#FAFAFA', 'fz': 30},
            # 128～2048 黄色系
            2**7: {'bg': '#FFEE58', 'fg': '#FAFAFA', 'fz': 30},
            2**8: {'bg': '#FFEB3B', 'fg': '#FAFAFA', 'fz': 30},
            2**9: {'bg': '#FDD835', 'fg': '#FAFAFA', 'fz': 30},
            # 1024~2048 橙色系
            2**10: {'bg': '#FF9800', 'fg': '#FAFAFA', 'fz': 30},
            2**11: {'bg': '#FB8C00', 'fg': '#FAFAFA', 'fz': 28},
            # 4096 +  红色系
            2**12: {'bg': '#fb3030', 'fg': '#FAFAFA', 'fz': 28},
            2**13: {'bg': '#e92e2e', 'fg': '#FAFAFA', 'fz': 28},
            2**14: {'bg': '#da1e1e', 'fg': '#FAFAFA', 'fz': 24},
            # 2**15 +  黑色 超过2**15颜色不再改变
            2**15: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 22},
            2**16: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 20},
            2**17: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 20},
            2**18: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 20},
            2**19: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 18},
            2**20: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 17},
            2**21: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 16},
            2**22: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 15},
            2**23: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 14},
            2**24: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 13},
            2**25: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 12},
        }
        self.data = Matrix2048(column)
        self.data.show()
        self.win = self.init_win()
        self.update_ui()
        self.win.bind('<Key>', self.key_event)
        self.win.mainloop()

    def init_win(self):
        win = tkinter.Tk()
        win.title('2048')
        winWidth = self.column * (self.cellSize + self.spaceSize) + self.spaceSize
        headerHeight = self.cellSize + 2 * self.spaceSize
        winHeight = winWidth + headerHeight
        screenWidth = win.winfo_screenwidth()
        screenHeight = win.winfo_screenheight()
        win.geometry(
            f'{winWidth}x{winHeight}+{int((screenWidth - winWidth) / 2)}+{int((screenHeight - winHeight) / 2)}')
        self.header = tkinter.Frame(win, height=headerHeight, width=winWidth)
        self.init_header()
        self.table = tkinter.Frame(win, height=winWidth, width=winWidth)
        self.init_table()
        return win

    def init_header(self):
        self.header['bg'] = self.style['page']['bg']
        # 分数
        elementScore = tkinter.Label(self.header, bd=0)
        elementScore['fg'] = '#707070'
        elementScore['bg'] = self.style['page']['bg']
        elementScore['font'] = ("黑体", 30, "bold")
        elementScore['text'] = 'score:' + str(self.data.score)
        elementScore.place(x=15, y=25)
        self.elementScore = elementScore
        self.header.pack()

    def init_table(self):
        self.table['bg'] = self.style['page']['bg']

        # 创建棋盘子元素
        elements = [[0 for x in range(self.column)] for y in range(self.column)]
        for row in range(self.column):
            for col in range(self.column):
                element = tkinter.Label(self.table, bd=0)
                img = Image.new(
                    'RGB', (self.cellSize, self.cellSize), self.style[0]['bg'])
                img = ImageTk.PhotoImage(img)
                element.configure(image=img)
                element['width'] = self.cellSize
                element['height'] = self.cellSize
                element['text'] = ''
                element['compound'] = 'center'

                y = self.spaceSize + (self.cellSize + self.spaceSize) * row
                x = self.spaceSize + (self.cellSize + self.spaceSize) * col

                element.place(x=x, y=y)
                elements[row][col] = element
        self.elements = elements
        self.table.pack()

    def key_event(self, event):
        if event.keysym in ['Up', 'w', 'Down', 's', 'Left', 'a', 'Right', 'd']:
            if event.keysym in ['Up', 'w']:  # 向上
                self.data.move_up()
            elif event.keysym in ['Down', 's']:  # 向下
                self.data.move_down()
            elif event.keysym in ['Left', 'a']:  # 向左
                self.data.move_left()
            elif event.keysym in ['Right', 'd']:  # 向右
                self.data.move_right()
            self.data.generate_number()
            self.data.show()
            self.update_ui()

        def reset_game():
            self.t = 0
            self.data.init()
            self.update_ui()

        if self.data.gameover() is True:
            print("here")
            self.t = 1
            # 直接结束显式存在问题，显示的是上次的数据
            if self.t == 0:
                self.t = 1
            else:
                res = tkinter.messagebox.askyesno(
                    title="2048", message="Game Over!\n是否重新开始!")
                if res is True:
                    reset_game()
                else:
                    self.win.quit()

    def update_ui(self):
        def update_score():
            self.elementScore['text'] = 'score:' + str(self.data.score)
        update_score()
        matrix = self.data.matrix
        for row in range(self.column):
            for col in range(self.column):
                num = matrix[row][col]
                element = self.elements[row][col]
                img = Image.new(
                    'RGB', (self.cellSize, self.cellSize), self.style[num]['bg'])
                img = ImageTk.PhotoImage(img)
                element.configure(image=img)
                element['fg'] = self.style[num]['fg']
                element['bg'] = self.style[num]['bg']
                element['image'] = img
                element['font'] = ("黑体", self.style[num]['fz'], "bold")
                element['text'] = str(num) if num != 0 else ''



if __name__ == '__main__':
    g = Game2048(4)
